Skip to main content

UAT Test Plan — Bestway CSM API Toolkit

FieldValue
Version1.0
DateMarch 5, 2026
ProjectBestway BC Development
EnvironmentBusiness Central Sandbox

Overview

This test plan validates the Bestway CSM API Toolkit extension for Microsoft Dynamics 365 Business Central. The extension publishes read-only OData v4 API endpoints under the bestway/csm/v1.0 API group, exposing service order data, fault reporting tables, posted document headers and lines, and document attachments. These endpoints support the CSM integration workflow, the Failed CSM Submissions Resolver, and CMS FAQ Management automated test validation.

The extension includes computed fields that look up data from related tables (Service Item, Fault Area, Symptom Code, Fault Code) and a fileUrls field that returns Record Link URLs as a JSON array. All endpoints reject POST, PATCH, and DELETE requests.

Extension Object Summary

ObjectIDExtendsPurpose
Page56101Service HeaderService Order API — read-only service order headers with computed csmQualitySent flag.
Page56102Service LineService Order Line API — service order lines with resolved fault descriptions and Service Item lookups.
Page56103Document AttachmentService Order Attachment API — document attachments with base64-encoded content.
Codeunit56104N/ACSM API Helper — extracted computed field logic for testability.
Page56123Service Invoice HeaderPosted Svc. Invoice Header API — posted invoice headers with fileUrls and subpage expansion.
Page56121Service Invoice LinePosted Svc. Invoice Line API — posted invoice lines with computed itemNo and salesDate fields.

Prerequisites

  • The Bestway CSM API Toolkit extension (v1.2.0.0) is published to the sandbox environment.
  • The BestwayUSA extension (v27.0.0.0, publisher: Innovia Consulting) is published as a dependency.
  • An Azure AD app registration is configured with API.ReadWrite.All permission for Business Central.
  • A BC user mapped to the app registration has Service Management read permissions.
  • Postman (or equivalent HTTP client) is installed with the BestwayCSM collection imported and OAuth2 configured.
  • At least 5 service orders exist in the sandbox, some with posted invoices.
  • At least 2 posted service invoices have Record Links (URL type) attached for fileUrls testing.
  • At least 1 posted service invoice line has a Service Item with Item No. and Sales Date populated.

Test Area 1: Service Order API

Endpoint: /serviceOrders with $expand for lines and attachments.

Verify the Service Order API returns correct data, computed fields resolve properly, and subpage expansion works.

TC-1.1: Service Orders Endpoint Returns Data

StepActionExpected Result
1Send GET request to /serviceOrders?$top=5.Response returns HTTP 200 with an array of up to 5 service order records.
2Inspect the response fields for each record.Each record contains: id, no, customerNo, orderDate, postingDate, csmQualitySent.
3Verify the csmQualitySent field for an order known to have a posted invoice with CSM Quality Sent = true.csmQualitySent = true for that order. The field is computed from the Service Invoice Header table.

TC-1.2: Service Order Subpage Expansion — Lines and Attachments

StepActionExpected Result
1Send GET request to /serviceOrders({id})?$expand=serviceOrderLines,serviceOrderAttachments for an order with at least one line and one attachment.Response returns HTTP 200 with the order record.
2Inspect the serviceOrderLines array.Lines are present with fields: id, no, serviceItemNo, itemNo, salesDate, faultAreaCode, faultAreaDescription, symptomCode, symptomCodeDescription, faultCode, faultCodeDescription.
3Inspect the serviceOrderAttachments array.Attachments are present with fields: id, documentNo, fileName, fileExtension, contentBase64.

TC-1.3: Service Order Line Computed Fields

StepActionExpected Result
1Identify a service order line with a Service Item that has Item No. and Sales Date populated.A suitable line is identified.
2Query the line via /serviceOrders({id})?$expand=serviceOrderLines or directly via /serviceOrderLines.The line is returned.
3Verify itemNo matches the Item No. on the Service Item record, and salesDate matches the Sales Date.Both computed fields are correctly resolved from the Service Item table.
4Verify faultAreaDescription, symptomCodeDescription, and faultCodeDescription are populated for lines with those codes set.All three description fields are resolved from their respective master tables.

Test Area 2: Posted Invoice Header API

Endpoint: /postedServiceInvoiceHeaders with fileUrls and $expand for lines.

Verify the Posted Service Invoice Header API returns correct header data, the fileUrls computed field, and subpage expansion to invoice lines.

TC-2.1: Posted Invoice Headers Endpoint Returns Data

StepActionExpected Result
1Send GET request to /postedServiceInvoiceHeaders?$top=5.Response returns HTTP 200 with an array of up to 5 posted invoice header records.
2Inspect the response fields.Each record contains: id, no, customerNo, orderNo, orderDate, postingDate, locationCode, csmQualitySent, fileUrls.
note

This test requires at least one posted invoice header with Link-type Record Links attached. If possible, also attach a Note-type Record Link to verify it is excluded.

StepActionExpected Result
1Identify a posted invoice header with at least one Link-type Record Link attached.A suitable header is identified.
2Query /postedServiceInvoiceHeaders?$filter=no eq '{invoiceNo}' for that header.The header is returned with the fileUrls field populated.
3Parse the fileUrls JSON array.The array contains one entry per Link-type Record Link, each with 'url' and 'type' keys. The 'type' value is '1'.
4If a Note-type Record Link was attached, verify it is NOT in the fileUrls array.Only Link-type Record Links appear in fileUrls. Notes are excluded.
StepActionExpected Result
1Identify a posted invoice header with no Record Links attached.A suitable header is identified.
2Query /postedServiceInvoiceHeaders?$filter=no eq '{invoiceNo}' for that header.The header is returned with fileUrls = '[]' (empty JSON array).

TC-2.4: Subpage Expansion — Posted Invoice Lines

StepActionExpected Result
1Send GET request to /postedServiceInvoiceHeaders({id})?$expand=postedServiceInvoiceLines for a header with at least one line.Response returns HTTP 200 with the header and an expanded postedServiceInvoiceLines array.
2Inspect the lines array.Lines contain: id, documentNo, lineNo, type, no, description, quantity, unitPrice, lineAmount, serviceItemNo, itemNo, salesDate.
3Verify the documentNo on each line matches the header's no field.All lines reference the correct parent header.

Test Area 3: Posted Invoice Line API

Endpoint: /postedServiceInvoiceLines with computed itemNo and salesDate.

Verify the Posted Invoice Line API returns correct line data and that the computed itemNo and salesDate fields resolve from the Service Item table.

TC-3.1: Posted Invoice Lines Endpoint Returns Data

StepActionExpected Result
1Send GET request to /postedServiceInvoiceLines?$top=10.Response returns HTTP 200 with an array of up to 10 posted invoice line records.
2Inspect the response fields.Each record contains: id, documentNo, lineNo, type, no, description, quantity, unitPrice, lineAmount, customerNo, orderNo, postingDate, serviceItemNo, serviceItemSerialNo, itemCategoryCode, faultAreaCode, symptomCode, faultCode, resolutionCode, faultReasonCode, bestwaySerialNo, faultCodeDescription, symptomCodeDescription, faultAreaDescription, itemNo, salesDate.

TC-3.2: Computed Fields — itemNo and salesDate

StepActionExpected Result
1Identify a posted invoice line with a Service Item that has Item No. and Sales Date populated.A suitable line is identified (serviceItemNo is not blank).
2Query /postedServiceInvoiceLines?$filter=serviceItemNo eq '{serviceItemNo}'.The line is returned with itemNo and salesDate fields populated.
3Cross-reference the values against the Service Item card in BC.itemNo matches Service Item → Item No. salesDate matches Service Item → Sales Date.

TC-3.3: Computed Fields — Blank When No Service Item

StepActionExpected Result
1Identify a posted invoice line with a blank Service Item No. (e.g., a G/L Account type line).A suitable line is identified.
2Query that line.itemNo is blank and salesDate is '0001-01-01' (BC zero date in OData).

Test Area 4: OData Filters and Query Options

Validate $filter, $top, $select, $orderby, and $count across key endpoints.

Verify that standard OData query options work correctly on the CSM API endpoints.

TC-4.1: $filter by Field Values

StepActionExpected Result
1Send GET /postedServiceInvoiceHeaders?$filter=csmQualitySent eq false and locationCode eq 'CS'.Response returns only headers where CSM Quality Sent is false and Location Code is 'CS'.
2Send GET /serviceOrders?$filter=customerNo eq '{knownCustomerNo}'.Response returns only orders for the specified customer.
3Send GET /serviceOrders?$filter=orderDate ge 2025-01-01 and orderDate le 2025-12-31.Response returns only orders within the specified date range.

TC-4.2: $top, $select, $orderby, and $count

StepActionExpected Result
1Send GET /serviceOrders?$top=3.Response returns exactly 3 records (or fewer if fewer than 3 exist).
2Send GET /serviceOrders?$select=no,customerNo.Response returns records with only the no and customerNo fields (plus id).
3Send GET /serviceOrders?$orderby=orderDate desc.Response returns records sorted by orderDate in descending order.
4Send GET /serviceOrders?$count=true.Response includes an @odata.count field with the total record count.

Test Area 5: Error Handling and Edge Cases

Verify the API behaves correctly for invalid inputs, non-existent records, and read-only enforcement.

These tests confirm the API returns appropriate responses for boundary conditions and rejects data modification attempts.

TC-5.1: Non-Existent Record Returns 404

StepActionExpected Result
1Send GET /serviceOrders(00000000-0000-0000-0000-000000000000).Response returns HTTP 404 (Not Found).
2Send GET /postedServiceInvoiceHeaders(00000000-0000-0000-0000-000000000000).Response returns HTTP 404 (Not Found).

TC-5.2: Empty Result Sets Return Empty Arrays

StepActionExpected Result
1Send GET /serviceOrders?$filter=no eq 'NONEXISTENT-ORDER-999'.Response returns HTTP 200 with an empty 'value' array.
2Send GET /postedServiceInvoiceLines?$filter=documentNo eq 'NONEXISTENT-INV-999'.Response returns HTTP 200 with an empty 'value' array.

TC-5.3: Write Operations Are Rejected

StepActionExpected Result
1Send POST /serviceOrders with a JSON body containing a minimal service order.Response returns HTTP 405 (Method Not Allowed) or HTTP 400. The record is not created.
2Send PATCH /serviceOrders({id}) with a field update.Response returns HTTP 405 (Method Not Allowed) or HTTP 400. The record is not modified.
3Send DELETE /serviceOrders({id}).Response returns HTTP 405 (Method Not Allowed) or HTTP 400. The record is not deleted.